home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Atari Mega Archive 1
/
Atari Mega Archive - Volume 1.iso
/
telecomm
/
sticpsrc.lzh
/
SOURCE.ARC
/
TCPIN.C
< prev
next >
Wrap
C/C++ Source or Header
|
1989-06-18
|
23KB
|
856 lines
/* Process incoming TCP segments. Page number references are to ARPA RFC-793,
* the TCP specification.
*/
#include "global.h"
#include "timer.h"
#include "mbuf.h"
#include "netuser.h"
#include "internet.h"
#include "tcp.h"
#include "icmp.h"
#include "iface.h"
#include "ip.h"
struct tcp_stat tcp_stat;
/* This function is called from IP with the IP header in machine byte order,
* along with a mbuf chain pointing to the TCP header.
*/
void
tcp_input(bp,protocol,source,dest,tos,length,rxbroadcast)
struct mbuf *bp; /* Data field, if any */
char protocol; /* Should always be TCP_PTCL */
int32 source; /* Remote IP address */
int32 dest; /* Our IP address */
char tos; /* Type of Service */
int16 length; /* Length of data field */
char rxbroadcast; /* Incoming broadcast - discard if true */
{
void reset(),update();
void proc_syn(),send_syn(),add_reseq(),get_reseq(),unlink_tcb();
register struct tcb *tcb; /* TCP Protocol control block */
struct tcp seg; /* Local copy of segment header */
struct connection conn; /* Local copy of addresses */
struct pseudo_header ph; /* Pseudo-header for checksumming */
int hdrlen; /* Length of TCP header */
if(bp == NULLBUF)
return;
if(rxbroadcast){
/* Any TCP packet arriving as a broadcast is
* to be completely IGNORED!!
*/
tcp_stat.bdcsts++;
free_p(bp);
return;
}
ph.source = source;
ph.dest = dest;
ph.protocol = protocol;
ph.length = length;
if(cksum(&ph,bp,length) != 0){
/* Checksum failed, ignore segment completely */
tcp_stat.checksum++;
free_p(bp);
return;
}
/* Form local copy of TCP header in host byte order */
if((hdrlen = ntohtcp(&seg,&bp)) < 0){
/* TCP header is too small */
tcp_stat.runt++;
free_p(bp);
return;
}
length -= hdrlen;
/* Fill in connection structure and find TCB */
conn.local.address = dest;
conn.local.port = seg.dest;
conn.remote.address = source;
conn.remote.port = seg.source;
if((tcb = lookup_tcb(&conn)) == NULLTCB){
struct tcb *ntcb;
void link_tcb();
/* Check that this segment carries a SYN, and that
* there's a LISTEN on this socket with
* unspecified source address and port
*/
conn.remote.address = 0;
conn.remote.port = 0;
if(!(seg.flags & SYN) || (tcb = lookup_tcb(&conn)) == NULLTCB){
/* No unspecified LISTEN either, so reject */
free_p(bp);
reset(source,dest,tos,length,&seg);
return;
}
/* We've found an server listen socket, so clone the TCB */
if(tcb->flags & CLONE){
if((ntcb = (struct tcb *)malloc(sizeof (struct tcb))) == NULLTCB){
free_p(bp);
/* This may fail, but we should at least try */
reset(source,dest,tos,length,&seg);
return;
}
ASSIGN(*ntcb,*tcb);
tcb = ntcb;
tcb->timer.arg = (char *)tcb;
tcb->timeout.arg = (char *)tcb;
} else
unlink_tcb(tcb); /* It'll be put back on later */
/* Stuff the foreign socket into the TCB */
tcb->conn.remote.address = source;
tcb->conn.remote.port = seg.source;
/* NOW put on right hash chain */
link_tcb(tcb);
/* Start the timeout on transient state */
/* This is to prevent TCB's hanging in "SYN Rcvd" state */
/* when no retour route exists to a calling host */
start_timer(&tcb->timeout);
}
/* Do unsynchronized-state processing (p. 65-68) */
switch(tcb->state){
case CLOSED:
free_p(bp);
reset(source,dest,tos,length,&seg);
return;
case LISTEN:
if(seg.flags & RST){
free_p(bp);
return;
}
if(seg.flags & ACK){
free_p(bp);
reset(source,dest,tos,length,&seg);
return;
}
if(seg.flags & SYN){
/* (Security check is bypassed) */
/* page 66 */
tcp_stat.conin++;
proc_syn(tcb,tos,&seg);
send_syn(tcb);
setstate(tcb,SYN_RECEIVED);
if(length != 0 || (seg.flags & FIN)) {
break; /* Continue processing if there's more */
}
tcp_output(tcb);
}
free_p(bp); /* Unlikely to get here directly */
return;
case SYN_SENT:
if(seg.flags & ACK){
if(!seq_within(seg.ack,tcb->iss+1,tcb->snd.nxt)){
free_p(bp);
reset(source,dest,tos,length,&seg);
return;
}
}
if(seg.flags & RST){ /* p 67 */
if(seg.flags & ACK){
/* The ack must be acceptable since we just checked it.
* This is how the remote side refuses connect requests.
*/
close_self(tcb,RESET);
}
free_p(bp);
return;
}
/* (Security check skipped here) */
/* Check incoming precedence; it must match if there's an ACK */
if((seg.flags & ACK) && PREC(tos) != PREC(tcb->tos)){
free_p(bp);
reset(source,dest,tos,length,&seg);
return;
}
if(seg.flags & SYN){
proc_syn(tcb,tos,&seg);
if(seg.flags & ACK){
/* Our SYN has been acked, otherwise the ACK
* wouldn't have been valid.
*/
update(tcb,&seg);
setstate(tcb,ESTABLISHED);
} else {
setstate(tcb,SYN_RECEIVED);
}
if(length != 0 || (seg.flags & FIN)) {
break; /* Continue processing if there's more */
}
tcp_output(tcb);
} else {
free_p(bp); /* Ignore if neither SYN or RST is set */
}
return;
}
/* We reach this point directly in any synchronized state. Note that
* if we fell through from LISTEN or SYN_SENT processing because of a
* data-bearing SYN, window trimming and sequence testing "cannot fail".
*/
/* Trim segment to fit receive window. */
if(trim(tcb,&seg,&bp,&length) == -1){
/* Segment is unacceptable */
if(!(seg.flags & RST)){
tcb->flags |= FORCE;
tcp_output(tcb);
}
return;
}
/* If segment isn't the next one expected, and there's data
* or flags associated with it, put it on the resequencing
* queue, ACK it and return.
*
* Processing the ACK in an out-of-sequence segment without
* flags or data should be safe, however.
*/
if(seg.seq != tcb->rcv.nxt
&& (length != 0 || (seg.flags & (SYN|FIN)) )){
add_reseq(tcb,tos,&seg,bp,length);
tcb->flags |= FORCE;
tcp_output(tcb);
return;
}
/* This loop first processes the current segment, and then
* repeats if it can process the resequencing queue.
*/
for(;;){
/* We reach this point with an acceptable segment; all data and flags
* are in the window, and the starting sequence number equals rcv.nxt
* (p. 70)
*/
if(seg.flags & RST){
if(tcb->state == SYN_RECEIVED
&& !(tcb->flags & (CLONE|ACTIVE))){
/* Go back to listen state only if this was
* not a cloned or active server TCB
*/
setstate(tcb,LISTEN);
} else {
close_self(tcb,RESET);
}
free_p(bp);
return;
}
/* (Security check skipped here) p. 71 */
/* Check for precedence mismatch or erroneous extra SYN */
if(PREC(tos) != PREC(tcb->tos) || (seg.flags & SYN)){
free_p(bp);
reset(source,dest,tos,length,&seg);
return;
}
/* Check ack field p. 72 */
if(!(seg.flags & ACK)){
free_p(bp); /* All segments after synchronization must have ACK */
return;
}
/* Process ACK */
switch(tcb->state){
case SYN_RECEIVED:
if(seq_within(seg.ack,tcb->snd.una+1,tcb->snd.nxt)){
update(tcb,&seg);
setstate(tcb,ESTABLISHED);
} else {
free_p(bp);
reset(source,dest,tos,length,&seg);
return;
}
break;
case ESTABLISHED:
case CLOSE_WAIT:
update(tcb,&seg);
break;
case FINWAIT1: /* p. 73 */
update(tcb,&seg);
if(tcb->sndcnt == 0){
/* Our FIN is acknowledged */
setstate(tcb,FINWAIT2);
}
break;
case FINWAIT2:
update(tcb,&seg);
break;
case CLOSING:
update(tcb,&seg);
if(tcb->sndcnt == 0){
/* Our FIN is acknowledged */
setstate(tcb,TIME_WAIT);
tcb->timer.start = SEC2TICK(MSL2);
start_timer(&tcb->timer);
}
break;
case LAST_ACK:
update(tcb,&seg);
if(tcb->sndcnt == 0){
/* Our FIN is acknowledged, close connection */
close_self(tcb,NORMAL);
return;
}
/* I think this is wrong, and can cause permanent ACK-ACK loops. dmf.
case TIME_WAIT:
tcb->flags |= FORCE;
start_timer(&tcb->timer);
*/
}
/* (URGent bit p